package sim1011.behaviours;

import jade.core.AID;
import jade.core.ContainerID;
import jade.core.behaviours.CyclicBehaviour;
import jade.domain.DFService;
import jade.domain.FIPAException;
import jade.domain.FIPAAgentManagement.DFAgentDescription;
import jade.domain.FIPAAgentManagement.ServiceDescription;
import jade.lang.acl.ACLMessage;
import jade.lang.acl.MessageTemplate;
import jade.wrapper.ControllerException;
import sim1011.components.Component;
import sim1011.starter.EctoMobileStarter;
import sim1011.starter.Logger;

public class ComponentBehaviour extends CyclicBehaviour implements
		java.io.Serializable {
	private static final long serialVersionUID = 4142998133598091844L;
	private AID[] poltergeist;
	private AID[] resMan;

	@Override
	public void action() {
		MessageTemplate tp = MessageTemplate
				.MatchPerformative(ACLMessage.PROPAGATE);
		// Messaggi di moveout
		ACLMessage msg = myAgent.receive(tp);
		if (msg != null) {
			Logger.log("[COMPONENT " + myAgent.getLocalName()
					+ "] received execute from: "
					+ msg.getSender().getLocalName() + " - Move out."
					+ " You are on mobile node! Go to: " + msg.getContent());
			ContainerID newContainer = new ContainerID();
			String[] contentQoS = parseWhereToExecute(msg.getContent());
			newContainer.setName(contentQoS[0]);
			((Component) myAgent).forcedMove = true;
			sendConsumeForMigration(contentQoS[1]);
			myAgent.doMove(newContainer);
		} else {
			msg = myAgent.receive();
			if (msg != null) {
				if (msg.getPerformative() == ACLMessage.INFORM) { // Messaggio
																	// di
																	// execute
																	// da
																	// poltergeist
																	// di dove
																	// eseguirmi
					//adesso nel messaggio che mi arriva devo ricavare la QoS
					String[] totContent = parseWhereToExecute(msg.getContent());
					String content = totContent[0];
					String QoS = totContent[1];
					Logger.log("[COMPONENT " + myAgent.getLocalName()
							+ "] received from poltergeist to go to: "
							+ content);
					try {
						if (content.equals(myAgent.getContainerController()
								.getContainerName())) {
							// execute here
							execute();
						} else {
							// move
							ContainerID cID = new ContainerID();
							cID.setName(content);
							if(myAgent.getContainerController().getContainerName().equals("Node.M"))
								sendConsumeForMigration(QoS);
							myAgent.doMove(cID);
						}
					} catch (ControllerException e) {
						e.printStackTrace();
					}
				} else if (msg.getPerformative() == ACLMessage.CFP) { // Messaggio
																		// di
																		// startup
					Logger.log("[COMPONENT " + myAgent.getLocalName()
							+ "[ Startup");
					execute();
				}
			}
			block();
		}
	}

	private void sendConsumeForMigration(String qoS) {
		ACLMessage consMsg = new ACLMessage(ACLMessage.UNKNOWN);
		try {
			findCurrentResourceManager();
		} catch (ControllerException e) {
			e.printStackTrace();
		} 
		consMsg.setContent(((Component) myAgent).getRequiredCPU() + "#" + ((Component) myAgent).getRequiredMemory() + "#" + qoS);
		Logger.log("[COMPONENT " + myAgent.getLocalName()
				+ "] sending consume resource to mobile node to migrate me");
		consMsg.addReceiver(resMan[0]);
		myAgent.send(consMsg);
	}

	private String[] parseWhereToExecute(String content) {
		return content.split("#");
	}

	public void execute() {
		loadAndExecuteOnResource();
		sendFinished();
	}

	private void findCurrentResourceManager() throws ControllerException {
		DFAgentDescription template = new DFAgentDescription();
		ServiceDescription sd = new ServiceDescription();
		sd.setType("ecto-resource");
		sd.setName("ecto-resource."
				+ myAgent.getContainerController().getContainerName());
		Logger.log("[COMPONENT " + myAgent.getLocalName() + "] looking for: "
				+ "ecto-resource."
				+ myAgent.getContainerController().getContainerName());
		template.addServices(sd);
		try {
			DFAgentDescription[] result = DFService.search(myAgent, template);
			resMan = new AID[result.length];
			for (int i = 0; i < result.length; ++i)
				// dovrebbe essere uno solo
				resMan[i] = result[i].getName();
		} catch (FIPAException e) {
			e.printStackTrace();
		}
	}

	public AID findPoltergeist() {
		DFAgentDescription template = new DFAgentDescription();
		ServiceDescription sd = new ServiceDescription();
		sd.setType("ecto-poltergeist");
		template.addServices(sd);
		try {
			DFAgentDescription[] result = DFService.search(myAgent, template);
			poltergeist = new AID[result.length];
			for (int i = 0; i < result.length; ++i)
				// dovrebbe essere uno solo
				poltergeist[i] = result[i].getName();
			return poltergeist[0];
		} catch (FIPAException e) {
			e.printStackTrace();
			return null;
		}
	}

	private void loadAndExecuteOnResource() {
		Logger.log("[COMPONENT " + myAgent.getLocalName()
				+ "] loading my code on node");
		try {
			findCurrentResourceManager();
		} catch (ControllerException e) {
			e.printStackTrace();
		}
		sendLoadAndExecute();
	}

	private void sendFinished() {
		DFAgentDescription template = new DFAgentDescription();
		ServiceDescription sd = new ServiceDescription();
		sd.setType("ecto-poltergeist");
		template.addServices(sd);
		try {
			DFAgentDescription[] result = DFService.search(myAgent, template);
			poltergeist = new AID[result.length];
			for (int i = 0; i < result.length; ++i)
				poltergeist[i] = result[i].getName();
		} catch (FIPAException e) {
			e.printStackTrace();
		}
		int who = EctoMobileStarter.r.nextInt(EctoMobileStarter.NUM_COMPONENTS);
		while (myAgent.getLocalName().equals("Comp." + who))
			who = EctoMobileStarter.r.nextInt(EctoMobileStarter.NUM_COMPONENTS);
		ACLMessage finishMsg = new ACLMessage(ACLMessage.INFORM);
		finishMsg.addReceiver(poltergeist[0]);
		finishMsg.setContent(Integer.toString(who));
		Logger.log("[COMPONENT " + myAgent.getLocalName()
				+ "] sending execute to poltergeist for: " + "Comp." + who);
		myAgent.send(finishMsg);
	}

	private void sendLoadAndExecute() {
		ACLMessage consMsg = new ACLMessage(ACLMessage.CONFIRM);
		consMsg.setContent(((Component) myAgent).getRequiredCPU() + "#"
				+ ((Component) myAgent).getRequiredMemory());
		Logger.log("[COMPONENT " + myAgent.getLocalName()
				+ "] found resource: " + resMan[0].getLocalName());
		consMsg.addReceiver(resMan[0]);
		myAgent.send(consMsg);
		MessageTemplate tp = MessageTemplate
				.MatchPerformative(ACLMessage.DISCONFIRM);
		myAgent.blockingReceive(tp);
	}
}